/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * MainFrame.java
 *
 * Created on 03-May-2011, 22:08:15
 */

package databaseupgrade;

import TimeRecording.Upgrade.DatabaseCredentialsType;
import TimeRecording.Upgrade.DatabaseType;
import TimeRecording.Upgrade.DatabaseUpdateType;
import TimeRecording.Upgrade.SqlCommandType;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JOptionPane;

/**
 *
 * @author david
 */
public class MainFrame extends javax.swing.JFrame {

    private List<SqlCommandType> getCommands(int src, int dst) {

        List<SqlCommandType> commands = new ArrayList<SqlCommandType>();

        int maxItem = versionCombo.getItemCount();

        for (int index = 0; index < maxItem; index++) {

            GuiDatabaseVersion version = (GuiDatabaseVersion)
                    (versionCombo.getItemAt(index));

            int versionId = version.getVersion().getVersionId();

            // Updating the database
            if (dst > src) {

                if (versionId <= src) {
                    // Not interested in SQL for the current or earlier versions
                    continue;
                }
                if (versionId > dst) {
                    // Not interested in SQL for versions beyond our target
                    continue;
                }

                for (SqlCommandType command : version.getVersion()
                        .getDeploy().getSqlCommand()) {
                    command.setLabel(Integer.toString(versionId));
                }
                
                // Add update commands
                commands.addAll(version.getVersion().
                        getDeploy().getSqlCommand());

            // Rolling the database back?
            } else if (dst < src) {

                if (versionId > src) {
                    // Not interested earlier than our target
                    continue;
                }
                if (versionId <= dst) {
                    // Not interested in SQL for version later than the current
                    continue;
                }

                for (SqlCommandType command : version.getVersion()
                        .getRollback().getSqlCommand()) {
                    command.setLabel(Integer.toString(versionId));
                }

                // Add rollback commands to our list
                commands.addAll(version.getVersion().
                        getRollback().getSqlCommand());
            }
        }

        return commands;
    }

    // <editor-fold defaultstate="collapsed" desc="GUI Friendly DatbaseCredentials">
    private class GuiDatabaseCredentials {

        private DatabaseCredentialsType credentials;

        public GuiDatabaseCredentials(DatabaseCredentialsType credentials) {
            this.credentials = credentials;
        }

        @Override
        public String toString() {
            return credentials.getDescription();
        }

        public DatabaseCredentialsType getCredentials() {
            return credentials;
        }
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="GUI Friendly DatbaseVersion">
    private class GuiDatabaseVersion {

        private DatabaseUpdateType update;

        public GuiDatabaseVersion(DatabaseUpdateType update) {
            this.update = update;
        }

        @Override
        public String toString() {
            return Integer.toString(update.getVersionId());
        }

        public DatabaseUpdateType getVersion() {
            return update;
        }
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="GUI Getters & Setters">
    private GuiDatabaseCredentials selectedDatabase;

    public GuiDatabaseCredentials getSelectedDatabase() {
        return selectedDatabase;
    }

    public void setSelectedDatabase(GuiDatabaseCredentials selectedDatabase) {
        GuiDatabaseCredentials oldSelectedDatabase = this.selectedDatabase;
        this.selectedDatabase = selectedDatabase;
        firePropertyChange("selectedDatabase", oldSelectedDatabase, selectedDatabase);
    }

    private GuiDatabaseVersion selectedVersion;

    public GuiDatabaseVersion getSelectedVersion() {
        return selectedVersion;
    }

    public void setSelectedVersion(GuiDatabaseVersion selectedVersion) {
        GuiDatabaseVersion oldSelectedVersion = this.selectedVersion;
        this.selectedVersion = selectedVersion;
        firePropertyChange("selectedVersion", oldSelectedVersion, oldSelectedVersion);
    }
    // </editor-fold>

    /** Creates new form MainFrame */
    public MainFrame(DatabaseType database) {
        
        initComponents();

        // Populate the "databases" combo

        ArrayList<GuiDatabaseCredentials> instances =
                new ArrayList<GuiDatabaseCredentials>();

        List<DatabaseCredentialsType> credentialsList =
                database.getDatabaseCredentialsList().getDatabaseCredentials();

        for (DatabaseCredentialsType credentials : credentialsList) {
            instances.add(new GuiDatabaseCredentials(credentials));
        }

        databaseCombo.setModel(new DefaultComboBoxModel(instances.toArray()));

        // Populate the "versions" combo

        ArrayList<GuiDatabaseVersion> versions = 
                new ArrayList<GuiDatabaseVersion>();

        List<DatabaseUpdateType> updateList =
                database.getDatabaseUpdateList().getDatabaseUpdate();

        for (DatabaseUpdateType update : updateList) {
            versions.add(new GuiDatabaseVersion(update));
        }

        versionCombo.setModel(new DefaultComboBoxModel(versions.toArray()));
        commandTableModel = new SqlCommandTableModel(null);
        commandTable.setModel(commandTableModel);

        databaseCombo.setSelectedIndex(0);
        setCommandTableAppearance();
    }

    private void setCommandTableAppearance() {

        commandTable.getColumnModel().getColumn(2).setCellRenderer(
                new SqlCommandStatusRenderer());

        commandTable.getColumnModel().getColumn(0).setPreferredWidth(100);
        commandTable.getColumnModel().getColumn(1).setPreferredWidth(500);
        commandTable.getColumnModel().getColumn(2).setPreferredWidth(100);

        commandTable.setRowHeight(20);
    }

    /** 
     * This method is called from within the constructor to initialise the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;
        bindingGroup = new org.jdesktop.beansbinding.BindingGroup();

        currentLabel = new javax.swing.JLabel();
        targetLabel = new javax.swing.JLabel();
        currentVersion = new javax.swing.JTextField();
        versionCombo = new javax.swing.JComboBox();
        databaseCombo = new javax.swing.JComboBox();
        databaseLabel = new javax.swing.JLabel();
        executionPanel = new javax.swing.JPanel();
        loadButton = new javax.swing.JButton();
        runAllButton = new javax.swing.JButton();
        runStepButton = new javax.swing.JButton();
        tableCommandResponsePanel = new javax.swing.JSplitPane();
        tablePanel = new javax.swing.JScrollPane();
        commandTable = new javax.swing.JTable();
        logPanel = new javax.swing.JSplitPane();
        commandPanel = new javax.swing.JScrollPane();
        commandArea = new javax.swing.JTextArea();
        responsePanel = new javax.swing.JScrollPane();
        responseArea = new javax.swing.JTextArea();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        getContentPane().setLayout(new java.awt.GridBagLayout());

        currentLabel.setText("Current Version");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
        getContentPane().add(currentLabel, gridBagConstraints);

        targetLabel.setText("Target Version");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(4, 8, 4, 4);
        getContentPane().add(targetLabel, gridBagConstraints);

        currentVersion.setEditable(false);
        currentVersion.setMinimumSize(new java.awt.Dimension(50, 20));
        currentVersion.setPreferredSize(new java.awt.Dimension(50, 20));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 8);
        getContentPane().add(currentVersion, gridBagConstraints);

        org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${selectedVersion}"), versionCombo, org.jdesktop.beansbinding.BeanProperty.create("selectedItem"));
        bindingGroup.addBinding(binding);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
        getContentPane().add(versionCombo, gridBagConstraints);

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${selectedDatabase}"), databaseCombo, org.jdesktop.beansbinding.BeanProperty.create("selectedItem"));
        bindingGroup.addBinding(binding);

        databaseCombo.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                databaseComboActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(8, 4, 4, 8);
        getContentPane().add(databaseCombo, gridBagConstraints);

        databaseLabel.setText("Database");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(8, 8, 4, 4);
        getContentPane().add(databaseLabel, gridBagConstraints);

        executionPanel.setBorder(javax.swing.BorderFactory.createCompoundBorder(javax.swing.BorderFactory.createTitledBorder("Script Execution"), javax.swing.BorderFactory.createEmptyBorder(0, 0, 6, 0)));
        executionPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 5, 0));

        loadButton.setText("Load SQL Script");
        loadButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                loadButtonActionPerformed(evt);
            }
        });
        executionPanel.add(loadButton);

        runAllButton.setText("Run Script");
        runAllButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                runAllButtonActionPerformed(evt);
            }
        });
        executionPanel.add(runAllButton);

        runStepButton.setText("Run Command");
        runStepButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                runStepButtonActionPerformed(evt);
            }
        });
        executionPanel.add(runStepButton);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.gridwidth = 4;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
        getContentPane().add(executionPanel, gridBagConstraints);

        tableCommandResponsePanel.setDividerLocation(120);
        tableCommandResponsePanel.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

        tablePanel.setViewportView(commandTable);

        tableCommandResponsePanel.setTopComponent(tablePanel);

        logPanel.setDividerLocation(120);
        logPanel.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
        logPanel.setResizeWeight(1.0);

        commandArea.setColumns(20);
        commandArea.setRows(5);
        commandPanel.setViewportView(commandArea);

        logPanel.setTopComponent(commandPanel);

        responseArea.setColumns(20);
        responseArea.setRows(5);
        responsePanel.setViewportView(responseArea);

        logPanel.setRightComponent(responsePanel);

        tableCommandResponsePanel.setBottomComponent(logPanel);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.gridwidth = 4;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weighty = 1.0;
        getContentPane().add(tableCommandResponsePanel, gridBagConstraints);

        bindingGroup.bind();

        java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        setBounds((screenSize.width-615)/2, (screenSize.height-475)/2, 615, 475);
    }// </editor-fold>//GEN-END:initComponents

    private void databaseComboActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_databaseComboActionPerformed

        if (selectedDatabase != null) {
            
            // Free up any connections associated with an old database object
            if (database != null) {
                database.tearDown();
            }

            database = new Database(selectedDatabase.getCredentials());
            currentVersion.setText(database.getDatabaseVersion());
        }
    }//GEN-LAST:event_databaseComboActionPerformed

    private void runAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_runAllButtonActionPerformed

        String[] options = new String[] { "Abort", "Retry", "Ignore" };

        // If no database selected then bale out
        if (database == null) {
            return;
        }

        // If there are no commands to execute then bale out
        if (commandTableModel.getCommands().isEmpty()) {
            return;
        }

        Iterator<SqlCommandType> nextCommand =
                commandTableModel.getCommands().iterator();
        SqlCommandType command = nextCommand.next();

        do {
            boolean success = database.execute(command);
            commandTableModel.refresh();
            
            if (!success) {

                int option = JOptionPane.showOptionDialog(
                        this, command.getResponse(),
                        "Database Error",
                        JOptionPane.YES_NO_CANCEL_OPTION,
                        JOptionPane.ERROR_MESSAGE,
                        null, options, options[0]);

                // If aborting then just bale out
                if (option == 0) {
                    break;

                // If retrying then skip the "advance"
                } else if (option == 1) {
                    continue;
                }
            }
            
            // Advance to next command if there is one
            if (nextCommand.hasNext()) {
                command = nextCommand.next();
            } else {
                command = null;
            }

        } while (command != null);

        if (command == null) {
            database.setDatabaseVersion(
                    getSelectedVersion().getVersion().getVersionId());
            database.commit();
        } else {
            database.rollback();
        }
    }//GEN-LAST:event_runAllButtonActionPerformed

    private void loadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadButtonActionPerformed

        if (database == null) {
            database = new Database(getSelectedDatabase().getCredentials());
        }

        int src = 0;
        if (database.getDatabaseVersion() != null) {
            src = Integer.parseInt(database.getDatabaseVersion());
        }
        int dst = getSelectedVersion().getVersion().getVersionId();

        List<SqlCommandType> commands = getCommands(src, dst);

        commandTableModel = new SqlCommandTableModel(commands);

        commandTable.setModel(commandTableModel);
        
        setCommandTableAppearance();
    }//GEN-LAST:event_loadButtonActionPerformed

    private void runStepButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_runStepButtonActionPerformed

        String[] options = new String[] { "Retry", "Ignore" };

        if (database == null) {
            return;
        }

        int selectedRow = commandTable.getSelectedRow();
        if (selectedRow == -1) {
            return;
        }

        SqlCommandType command =
                commandTableModel.getSelectedCommand(selectedRow);

        boolean success = false;
        boolean retry = false;

        do {
            success = database.execute(command);
            commandTableModel.refresh();

            if (!success) {
                int option = JOptionPane.showOptionDialog(
                        this, command.getResponse(),
                        "Database Error",
                        JOptionPane.YES_NO_OPTION,
                        JOptionPane.ERROR_MESSAGE,
                        null, options, options[0]);

                retry = (option == 0);
            }

        } while (!success && retry);

        if (success) {
            database.commit();
        } else {
            database.rollback();
        }
    }//GEN-LAST:event_runStepButtonActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTextArea commandArea;
    private javax.swing.JScrollPane commandPanel;
    private javax.swing.JTable commandTable;
    private javax.swing.JLabel currentLabel;
    private javax.swing.JTextField currentVersion;
    private javax.swing.JComboBox databaseCombo;
    private javax.swing.JLabel databaseLabel;
    private javax.swing.JPanel executionPanel;
    private javax.swing.JButton loadButton;
    private javax.swing.JSplitPane logPanel;
    private javax.swing.JTextArea responseArea;
    private javax.swing.JScrollPane responsePanel;
    private javax.swing.JButton runAllButton;
    private javax.swing.JButton runStepButton;
    private javax.swing.JSplitPane tableCommandResponsePanel;
    private javax.swing.JScrollPane tablePanel;
    private javax.swing.JLabel targetLabel;
    private javax.swing.JComboBox versionCombo;
    private org.jdesktop.beansbinding.BindingGroup bindingGroup;
    // End of variables declaration//GEN-END:variables

    private SqlCommandTableModel commandTableModel;
    private Database database;
}
